{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Callisto tket framework\n",
"\n",
"The tket framework is a quantum software framework primarily used for the development and execution of quantum algorithms (quantum circuits). It is designed to be platform-agnostic software, and there are a lot of extensions available which allow tket to interface with backends from a range of providers ( see tket extension ).\n",
"\n",
"Due to its popularity and the broad amount of available backends using this framework, we wrote an extension to tket that enables a user to communicate with the C12 emulator Callisto . The backend name is `CallistoBackend`.\n",
"\n",
"We need to create a circuit using the `Circuit` class to run a quantum algorithm."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[X q[0];, CX q[0], q[1];]\n"
]
}
],
"source": [
"from pytket import Circuit\n",
"\n",
"circuit = Circuit(2)\n",
"circuit.X(0)\n",
"circuit.CX(0, 1)\n",
"\n",
"print(circuit.get_commands())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Internally all circuits in the pytket framework are represented as directed acyclic graphs (DAG). Using ptyket util package it is possible to see the corresponding DAG of the chosen circuit."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A classical model for quantum computing is a model where the main program is run on the host computer, which occasionally sends off jobs to the quantum computer and retrieves the results. A backend represents a connection to some instance, either quantum hardware or an emulator. It presents a uniform interface for submitting circuits to be processed and retrieving the results. The main goal of this approach is to promote the development of platform-independent software, helping the code that the developers write to be more future-proof.\n",
"\n",
"Using tket's abstract class, `Backend,` it is possible to set different characteristics of a device that it should represent. The main properties that a backend need to implement are:\n",
"\n",
"1. The restrictions that are specific to specific quantum devices. Different constraints are encoded using the predicates, which are essentially a boolean property of a circuit that must return True to run the circuit on the target machine.\n",
"\n",
"2. Sending a circuit to the target device and examining the results. This process can be accomplished by calling `Backend.process_circuit()` or `Backend.process_circuits()` methods. These methods will send a circuit for execution and return an instance of `ResultHandle` as a result of their execution. The `ResultHandle` is a unique identifier that can be used to retrieve the actual results once the job has finished.\n",
"\n",
"3. Retrieval of the results. Obtaining the results of a successfully run job\n",
" is done using the `Backend.get_result()` method, which returns an instance of `BackendResult` class. The class `BackendResult` has methods that can be useful for obtaining different pieces of information about the job that has been run. These methods are:\n",
"`get_state()` - get the state vector\n",
"`get_shots()` - get the shots. The shots are returned as a 2D array of the result for each shot.\n",
"`get_counts()` - get the counts (it is obtained from the shots directly)\n",
"`get_density_matrix()` - get the density matrix of the job.\n",
"\n",
"\n",
"To implement the C12 emulator Callisto, we have developed `CallistoBackend` class with all the above options incorporated."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"('ffb8e2ff-307a-4f02-8063-13b950a8dafe',)\n"
]
}
],
"source": [
"import os\n",
"from c12_callisto_clients.pytket.extensions.callisto import CallistoBackend\n",
"\n",
"access_token = os.getenv(\"C12_TOKEN\") # Token that is obtained for allowing the access to the system\n",
"backend_name = \"c12sim-iswap\"\n",
"\n",
"# create callisto backend instance\n",
"callisto = CallistoBackend(backend_name, token=access_token, verbose=False)\n",
"\n",
"# Get the ResultHandle instance\n",
"handle = callisto.process_circuit(circuit, n_shots=1024)\n",
"print(handle)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CircuitStatus(status=, message='', error_detail=None, completed_time=None, queued_time=None, submitted_time=None, running_time=None, cancelled_time=None, error_time=None, queue_position=None)\n",
"[[0 0]\n",
" [1 0]\n",
" [0 1]\n",
" ...\n",
" [1 1]\n",
" [1 1]\n",
" [1 1]]\n",
"Counter({(1, 1): 1021, (0, 0): 1, (0, 1): 1, (1, 0): 1})\n"
]
}
],
"source": [
"job_uuid = handle[0]\n",
"\n",
"# Get BackendResult instance\n",
"job_result = callisto.get_result(handle)\n",
"\n",
"\n",
"print(callisto.circuit_status(handle)) # Get the status of the circuit\n",
"print(job_result.get_shots())\n",
"print(job_result.get_counts())"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [
{
"ename": "CircuitNotValidError",
"evalue": "Circuit with index 0 in submitted does not satisfy MaxNQubitsPredicate(13) (try compiling with backend.get_compiled_circuits first).",
"output_type": "error",
"traceback": [
"\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
"\u001B[0;31mCircuitNotValidError\u001B[0m Traceback (most recent call last)",
"\u001B[0;32m/var/folders/kf/wdsyjzyj4vzf0cclsfx3swgh0000gn/T/ipykernel_39837/1469770625.py\u001B[0m in \u001B[0;36m\u001B[0;34m\u001B[0m\n\u001B[1;32m 4\u001B[0m \u001B[0mcircuit2\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mCX\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;36m0\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mn\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 5\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m----> 6\u001B[0;31m \u001B[0mhandle2\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mcallisto\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mprocess_circuit\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mcircuit2\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mn_shots\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0;36m1024\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 7\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 8\u001B[0m \u001B[0;31m# This should fail due to MaxNQubitsPredicate which controls the number of qubits that can be run on the emulator\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
"\u001B[0;32m/opt/anaconda3/lib/python3.9/site-packages/c12_callisto_clients/pytket/extensions/callisto/backends/callisto.py\u001B[0m in \u001B[0;36mprocess_circuit\u001B[0;34m(self, circuit, n_shots, valid_check, **kwargs)\u001B[0m\n\u001B[1;32m 313\u001B[0m \"\"\"\n\u001B[1;32m 314\u001B[0m \u001B[0;32mif\u001B[0m \u001B[0mvalid_check\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 315\u001B[0;31m \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m_check_all_circuits\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0mcircuit\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 316\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 317\u001B[0m \u001B[0mn_shots\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0;36m1024\u001B[0m \u001B[0;32mif\u001B[0m \u001B[0mn_shots\u001B[0m \u001B[0;32mis\u001B[0m \u001B[0;32mNone\u001B[0m \u001B[0;32melse\u001B[0m \u001B[0mn_shots\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
"\u001B[0;32m/opt/anaconda3/lib/python3.9/site-packages/pytket/backends/backend.py\u001B[0m in \u001B[0;36m_check_all_circuits\u001B[0;34m(self, circuits, nomeasure_warn)\u001B[0m\n\u001B[1;32m 123\u001B[0m )\n\u001B[1;32m 124\u001B[0m \u001B[0;32mfor\u001B[0m \u001B[0merror\u001B[0m \u001B[0;32min\u001B[0m \u001B[0merrors\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 125\u001B[0;31m \u001B[0;32mraise\u001B[0m \u001B[0merror\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 126\u001B[0m \u001B[0;32mif\u001B[0m \u001B[0mnomeasure_warn\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 127\u001B[0m \u001B[0;32mif\u001B[0m \u001B[0mcirc\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mn_gates_of_type\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mOpType\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mMeasure\u001B[0m\u001B[0;34m)\u001B[0m \u001B[0;34m<\u001B[0m \u001B[0;36m1\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n",
"\u001B[0;31mCircuitNotValidError\u001B[0m: Circuit with index 0 in submitted does not satisfy MaxNQubitsPredicate(13) (try compiling with backend.get_compiled_circuits first)."
]
}
],
"source": [
"circuit2 = Circuit(20)\n",
"for n in range(1, 20):\n",
" circuit2.X(n)\n",
" circuit2.CX(0, n)\n",
"\n",
"handle2 = callisto.process_circuit(circuit2, n_shots=1024)\n",
"\n",
"# This should fail due to MaxNQubitsPredicate which controls the number of qubits that can be run on the emulator"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.13"
}
},
"nbformat": 4,
"nbformat_minor": 4
}